-- ///////////////////////////////////////////////////////////////////////////////////////////////////
--
-- Custom Tools-DoWPropertiesBrowser
--
-- Last Update: 08/02/2005
-- Author: Claudio Alvaro (calvaro@relic.com)
-- Description: Displays DoW user defined properties and allows user to toggle them on or off.
--				Depressing the column header toggles the property in question for all selected nodes.
--
--
-- ////////////////////////////////////////////////////////////////////////////////////////////////////
macroScript DoWPropertiesBrowser
category:"Custom Tools"
toolTip:"DoW Properties Browser"
icon:#("Custom_Tools",12)
(
-- Rollout definition
global lv_rollout
-- Used to define size of dialog window
local lvWidth = 0
local lvHeight = 410
-- ListView headers
local layout_def = #(#("Object Name", 180),#("DoW Coords",150),#("Max Bone",60),#("Stale",40,"Stale"),#("Invisible",52,"ForceInvisible"),#("IgnoreShaders",83,"IgnoreShaders"),#("ForceSkinning",80,"ForceSkinning"),#("NoChild",53,"No_Child_Selection"),#("Remove",53,"Remove"))
-- Set width of dialog window based on listview column widths
for i = 1 to layout_def.count do
		(
		lvWidth += layout_def[i][2]
		)
lvWidth += 18
rollout lv_rollout "DoW Properties Browser"
	(
	---------------------------------------------------------------------------
	-- Converts translation coords to DoW Coords
	fn convertPos pos =
		(
		cpos = [-pos.x,pos.z,-pos.y]
		return cpos
		)
	---------------------------------------------------------------------------
	-- rounds a value to n decimal places
	fn round_to val n = 
		(
	    local mult = 10.0 ^ n
		(floor ((val * mult) + 0.5)) / mult
		)
	---------------------------------------------------------------------------
	-- creates a 15x16 icon from an existing icons bitmap
	fn getIconFromBitmap thePath number =
		(
		tempBmp = openBitmap thePath
		iconBmp = bitmap 16 15
		for v = 0 to 14 do
		setPixels iconBmp [0,v] (getPixels tempBmp [(number-1)*16, v] 16)
		iconBmp.filename = getDir #image +"/_temp.bmp"
		save iconBmp
		close iconBmp 
		close tempBmp
		getDir #image +"/_temp.bmp"
		)

	---------------------------------------------------------------------------
	-- Initialise listview.
	fn initListView lv ilTv =
		(
		/*
		ilTv.imagewidth = 16
		ilTv.imageheight = 15
		iconDir = (getDir #ui) + "\\icons"
		ilTv.listImages.add 1 #root (loadPicture (getIconFromBitmap (iconDir + "\\Standard_16i.bmp") 2))
		ilTv.listImages.add 2 #geomnode (loadPicture (getIconFromBitmap (iconDir + "\\Standard_16i.bmp") 1))
		ilTv.listImages.add 3 #light (loadPicture (getIconFromBitmap (iconDir + "\\Lights_16i.bmp") 3))
		ilTv.listImages.add 4 #camera (loadPicture (getIconFromBitmap (iconDir + "\\Cameras_16i.bmp") 2))
		ilTv.listImages.add 5 #helper (loadPicture (getIconFromBitmap (iconDir + "\\Helpers_16i.bmp") 1))
		
		lv.columnheadericons = ilTv
		lv.icons = ilTv
		*/	
		
		lv.View = #lvwReport
		lv.gridLines = true
		
		 
		lv.multiselect = true
		lv.sorted = true
		lv.sortOrder = #lvwAscending
		lv.labelEdit = #lvwManual
		lv.appearance = #ccFlat
		lv.borderStyle = #ccFixedSingle
		lv.fullRowSelect = true
		lv.backColor = color 177 240 239
	
		for i in layout_def do
			(
			column = lv.columnHeaders.add()
			--column.icon = 1
			column.text = i[1]
			)
		LV_FIRST = 0x1000
 		LV_SETCOLUMNWIDTH = (LV_FIRST + 30) 

		for i = 0 to layout_def.count-1 do 
  			windows.sendMessage lv.hwnd LV_SETCOLUMNWIDTH i layout_def[1+i][2]
		
		
		)

	---------------------------------------------------------------------------	
	fn getTxtInfo =
		(
		count = 0
		bip_count = 0
		for i in selection do
			if i.boneEnable == true then count += 1
			else if classOf i == Biped_Object do
				( 
				if ((findstring i.name "Footsteps") == undefined) do 
					(
					count += 1
					bip_count += 1
					)
				)
		
		("< " + (count as string) + " total bones in current selection >< " + (bip_count as string) + " biped objects >< " + (count - bip_count) as string + " bone objects >")
		)
		
	
	---------------------------------------------------------------------------	
	-- Checks for the existance of a user defined property.
	fn getDowProp obj propName =
		(
		if (val = getUserProp obj propName) != undefined do
			if (classOf val == String and val as name == #yes) then return "Yes"
			else return (val as string)
		return "--"
		)
	---------------------------------------------------------------------------
	-- Removes a user defined property when the user toggles the corresponding cell in the listview.
	fn removeUserProp obj testStr =
		(
		s = (getUserPropBuffer obj) as stringStream
		outStr = "" as stringStream
		seek s 0
		while (not (eof s)) do
			(
			line = readline s
			
			fstr = filterString line "= "
			if fstr.count > 2 then
				(
				messageBox (obj.name + " has inproper user defined property format. User defined property stripped, please redefine.") title:"DoW Properties Error" beep:false
				format "\r\n" to:outStr
				)
			else 
				if fstr[1] != undefined do
					if (findString fstr[1] testStr) == undefined do
						format "%\r\n" line to:outStr
			)
		
		setUserPropBuffer obj (outStr as string)
		)
	---------------------------------------------------------------------------
	-- Adds a user defined property when the user toggles the corresponding cell in the listview.
	fn addUserProp obj testStr =
		(
		s = (getUserPropBuffer obj) as stringStream
		outStr = "" as stringStream
		seek s 0
		while (not (eof s)) do
			(
			line = readline s
			
			fstr = filterString line "= "
			if fstr.count > 2 then
				(
				messageBox (obj.name + " has inproper user defined property format. User defined property stripped, please redefine.") title:"DoW Properties Error" beep:false
				format "\r\n" to:outStr
				)
			else
				if fstr[1] != undefined do
					format "%\r\n" line to:outStr
			)
		setUserPropBuffer obj (outStr as string)
		setUserProp obj testStr "Yes"		
		)
	---------------------------------------------------------------------------
	-- Toggles bone state, and also sets other properties to default.
	fn toggleBoneProp obj =
		(
		if (obj.boneEnable) then 
			obj.boneEnable = false
		else
			(
			obj.boneEnable = true
			obj.boneFreezeLength = true
			obj.boneAutoAlign = false
			obj.boneScaleType = #none
			)
		)
	---------------------------------------------------------------------------
	-- Toggles user properties by calling either addUserProp or removeUserProp functions.
	fn toggleDowProp li prop =
		(
		obj = getNodeByName (li.text)
		if obj != undefined do
			(		
			if li.listSubItems[prop].text == "--" then
				(
				case prop of
					(
					2 : (li.listSubItems[prop].text = "Yes" ;
						toggleBoneProp obj)
					3 : (li.listSubItems[prop].text = "Yes" ;
						addUserProp obj "Stale")
					4 : (li.listSubItems[prop].text = "Yes" ;
						addUserProp obj "ForceInvisible")
					5 : (li.listSubItems[prop].text = "Yes" ;
						addUserProp obj "IgnoreShaders")
					6 : (li.listSubItems[prop].text = "Yes" ;
						addUserProp obj "ForceSkinning")
					7 : (li.listSubItems[prop].text = "Yes" ;
						addUserProp obj "No_Child_Selection")
					8 : (li.listSubItems[prop].text = "Yes" ;
						addUserProp obj "Remove")
	
					default: ()
					)
				)
			else 
				(
				case prop of
					(
					2 : (li.listSubItems[prop].text = "--" ;
						toggleBoneProp obj)
					3 : (li.listSubItems[prop].text = "--" ;
						removeUserProp obj "Stale")
					4 : (li.listSubItems[prop].text = "--" ;
						removeUserProp obj "ForceInvisible")
					5 : (li.listSubItems[prop].text = "--" ;
						removeUserProp obj "IgnoreShaders")
					6 : (li.listSubItems[prop].text = "--" ;
						removeUserProp obj "ForceSkinning")
					7 : (li.listSubItems[prop].text = "--" ;
						removeUserProp obj "No_Child_Selection")
					8 : (li.listSubItems[prop].text = "--" ;
						removeUserProp obj "Remove")
	
					default: ()
					)
				)
			)
		)
	---------------------------------------------------------------------------	
	-- Fills listview with current selection.
	fn populateListView lv =
		(
		lv.listItems.clear()
		for i in selection do
			(
			-- color code text by class of node
			local classColor = (color 0 0 0)
			
			iconType = case superClassOf i of
				(
				GeometryClass:2
				Light:3
				Camera:4
				Helper:5
				Default:1
				)
				
			if classOf i == Geometry do classColor = (color 128 0 0)			
			if superClassOf i == Helper do classColor = (color 0 128 0)
			if classOf i == BoneGeometry do classColor = (color 255 0 0)
			if classOf i == Biped_Object do classColor = (color 0 0 255)
			
			-- add first row
			li = lv.listItems.add()
			-- add node name to first column 
			li.foreColor = classColor
			--li.icon = iconType
			li.text = i.name
			li.toolTipText = i.name + " is a " + (classOf i as string)

			-- add DoW property to second column 
			sub_li = li.listSubItems.add()
			sub_li.foreColor = classColor
			-- if enabled through checkbox
			if lv_rollout._checkDoWCoords.state == true then
				(
				local nodePosition
				
				if classOf i.controller == BipSlave_Control or classOf i.controller == Vertical_Horizontal_Turn then
					nodePosition = convertPos (biped.getTransform i #pos)
				else nodePosition = convertPos (i.position)
				
				sub_li.text = "X: " + (round_to nodePosition.x 3) as string + " Y: " + (round_to nodePosition.y 3) as string + " Z: " + (round_to nodePosition.z 3) as string
				sub_li.toolTipText = "DoW Coordinates enabled"
				)
			else 
				(
				sub_li.text = "--"
				sub_li.toolTipText = "DoW Coordinates disabled"
				)	
			
			sub_li = li.listSubItems.add()
			sub_li.toolTipText = "Bone property of " + i.name
			sub_li.foreColor = classColor
			if i.boneEnable then
				sub_li.text = "Yes"
			else sub_li.text = "--"
						
			for j = 4 to layout_def.count do
				(
				sub_li = li.listSubItems.add()
				sub_li.foreColor = classColor
				sub_li.text = try (getDowProp i layout_def[j][3]) catch ("--")
				sub_li.toolTipText = layout_def[j][3] + " property of " + i.name
				)
			)
		)
	---------------------------------------------------------------------------	
	-- Save user defined properties for selected nodes to text file.
	fn saveDowProp nodes =
		(
		local fStream, fPath
		
		if (fPath = getSaveFileName filename:"DoWProperties.txt" caption:"Save DoWProperties") != undefined do
			(
			if (fStream = openfile fPath mode:"w") != undefined do
				(
				flush fStream
				seek fStream 0
				for i in nodes do
					(
					print i.name to:fStream
					print (getUserPropBuffer i) to:fStream
					)
				close fStream
				)
			)
		)

	---------------------------------------------------------------------------	
	-- Load user defined properties from text file.
	fn loadDowProp =
		(
		local fStream, fPath
		
		if (fPath = getOpenFileName filename:"DoWProperties.txt" caption:"Load DoWProperties") != undefined do
			(
			if (fStream = openFile fPath mode:"r") != undefined do
				(
				flush fStream
				seek fStream 0
				
				while (not (eof fStream)) do
					(
					if (node = getNodeByName(readValue fStream)) != undefined then
						(
						format "%\n" node -- output to listener
						
						local str = "" as stringStream
						local s = (readValue fStream) as stringStream
						seek s 0
						while (not (eof s)) do
							(
							line = readLine s
							format "%\r\n" line to:str
							format "\t-%\n" line -- output to listener
							)
						
						setUserPropBuffer node (str as string)
						)
					else readValue fStream
					)
				close fStream
				)
			)
		)
	---------------------------------------------------------------------------
	-- for DoW Coord callback during time change or scrubbing
	fn timeCallBackFunction = 
		(
		lv_rollout.populateListView lv_rollout.lv
		)
	---------------------------------------------------------------------------
	-- Button definitions and activeX controls.
	edittext _txtInfo readOnly:true height:21 pos:[2,4]
	button _btnSave "Save" width:60 height:21 pos:[2,28] tooltip:"Save DoW Properties to file"
	button _btnLoad "Load" width:60 height:21 pos:[66,28] tooltip:"Load DoW Properties from file"

	button _btnClear "Clear" width:60 height:21 pos:[130,28] tooltip:"Clear all DoW Properties on selected nodes"
	
	checkbox _checkDoWCoords "Display DoW Coordinates" pos:[200,32] default:false
	
	activeXControl ilTv "MSComctlLib.ImageListCtrl" height:0 width:0
	activeXControl lv "MSComctlLib.ListViewCtrl.2" pos:[0,56]	
	
	on _btnSave pressed do
		(
		saveDowProp (selection as array)
		)
	
	on _btnLoad pressed do
		(
		loadDowProp()
		populateListView lv
		_txtInfo.text = getTxtInfo()
		)
	
	on _checkDoWCoords changed state do
		(
		if state == true then
			(
			registerTimeCallBack timeCallBackFunction
			)
		else
			(
			unRegisterTimeCallBack timeCallBackFunction
			gc()
			)
		populateListView lv
		_txtInfo.text = getTxtInfo()
		)
	
	on _btnClear pressed do
		(
		if queryBox "This will clear all DoW Properties for the selected nodes. Continue?" beep:false do
			(
			for i in selection do 
				setUserPropBuffer i ""
			populateListView lv
			_txtInfo.text = getTxtInfo()
			)
		)
		
	on lv mouseDown button shift x y do
		(
		hit = listView.hitTest lv.hWnd [x,y]
		-- gather list of highlighted items
		highlightedListItems = for i = 1 to lv.listItems.count 
			where (lv.listItems[i].selected == true) 
				collect lv.listItems[i]
		
		if hit[1] != 0 and hit[2] >= 2 do
			(
			if highlightedListItems.count > 1 then
				for i in highlightedListItems do 
					toggleDowProp i hit[2]
			else toggleDoWProp lv.listItems[hit[1]] hit[2]
			)
		_txtInfo.text = getTxtInfo()
		)
	
	on lv columnClick column do
		(
		if column.index >= 3 do
			if selection.count > 0 do
				for i = 1 to selection.count do
					toggleDowProp lv.listItems[i] (column.index-1)
		_txtInfo.text = getTxtInfo()
		)
	
	on lv_rollout open do
		(
		lv.size = [(lv_rollout.width), (lv_rollout.height-62)]
		for i in geometry do
			(
			i.boneFreezeLength = true
			i.boneAutoAlign = false
			i.boneScaleType = #none
			)
		_txtInfo.text = getTxtInfo()	
		initListView lv ilTv
		populateListView lv
		)
	
	on lv_rollout resized pos do
		(
		lv.size = [(lv_rollout.width), (lv_rollout.height-62)]
		)
		
	on lv_rollout close do 
		(
		callbacks.removeScripts #selectionSetChanged id:#SceneListView
		callbacks.removeScripts #selectionSetChanged id:#TxtInfo
		unRegisterTimeCallBack timeCallBackFunction
		)
	
	) -- end rollout
	
try(destroyDialog lv_rollout)catch()

createDialog lv_rollout width:lvWidth height:lvHeight style:#(#style_resizing, #style_titlebar, #style_sysmenu, #style_minimizebox, #style_maximizebox)

-- Callback will repopulate lisview when selection changes.
callbacks.addScript #selectionSetChanged "lv_rollout.populateListView lv_rollout.lv" id:#SceneListView
callbacks.addScript #selectionSetChanged "lv_rollout._txtInfo.text = lv_rollout.getTxtInfo()" id:#TxtInfo

) -- end macroscript
